/*
 * Created on Jan 12, 2009
 */
package com.mgmaps.utils;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import com.mgmaps.app.APPCanvas;

/**
 * Button overlay handling.
 */
public class ButtonOverlay {
  private final int type;
  private final int alignType;
  private final int numButtons;

  private final Image[] images;
  private final Image[] images2;
  private final Image[] images3;
  private int imageW;
  private int imageH;
  
  private String[] texts;
  private Font font;
  private int[] textWidth;
  private int textHeight;
  
  private int totWidth;
  private int totHeight;
  
  public int selected;
  private final static int DEFAULTHSPACING = 10;
  private final static int DEFAULTVSPACING = 2;
  private final static int SPACING_TEXT = 2;
  public final static int SPACING_DEFAULT = -1;
  
  private int spacing;
  private boolean paintBackground;
  private int selectionMode;
  private boolean pressed;
  
  public final static int SELECT_NONE = 0; 
  public final static int SELECT_RECTANGLE = 1;
  // public final static int SELECT_IMAGE = 2;
  
  private int mapWidth;
  private int mapHeight;
  private int scrolled;
  
  private final boolean pointer;
  
  public static final int ALIGN_HORIZONTAL = 0;
  public static final int ALIGN_VERTICAL = 1;
  public static final int ALIGN_VERTICAL_SCROLL = 2;

  public ButtonOverlay(int type, int alignType, Image[] images, String[] texts, int mapWidth,
      int mapHeight) {
    this(type, alignType, images, texts, mapWidth, mapHeight, SPACING_DEFAULT, true, SELECT_RECTANGLE,
        images, images);
  }

  public ButtonOverlay(int type, int alignType, Image[][] images, String[] texts, int mapWidth,
      int mapHeight, int spacing, boolean paintBackground, int selectionMode) {
    this(type, alignType, images[0], texts, mapWidth, mapHeight, spacing, paintBackground,
        selectionMode, images[1], images[2]);
  }
  
  protected ButtonOverlay(int type, int alignType, Image[] images, String[] texts, int mapWidth,
      int mapHeight, int spacing, boolean paintBackground, int selectionMode, Image[] images2,
      Image[] images3) {
    this.type = type;
    this.alignType = alignType;
    this.images = images;
    this.texts = texts;
    this.numButtons = images.length;
    this.selected = 0;
    this.imageW = images[0].getWidth();
    this.imageH = images[0].getHeight();
    this.mapWidth = mapWidth;
    this.mapHeight = mapHeight;
    this.paintBackground = paintBackground;
    this.selectionMode = selectionMode;
    this.images2 = images2;
    this.images3 = images3;
    this.pointer = APPCanvas.instance.hasPointerEvents();
    
    if (spacing < 0) {
      if (alignType == ALIGN_HORIZONTAL)
        this.spacing = DEFAULTHSPACING;
      else if (alignType == ALIGN_VERTICAL || alignType == ALIGN_VERTICAL_SCROLL)
        this.spacing = DEFAULTVSPACING;
    }
    else
      this.spacing = spacing;

    updateWidthHeight();
  }
  
  private void updateWidthHeight() {
    // pre-compute widths
    int totw = 0;
    if (texts != null) {
      font = Font.getFont(Font.FACE_PROPORTIONAL, Font.SIZE_MEDIUM, Font.STYLE_PLAIN);
      final int[] tw = new int[numButtons];
      for (int i=0; i<numButtons; i++) {
        tw[i] = font.stringWidth(texts[i]) + (SPACING_TEXT<<1);
        totw += tw[i];
      }
      textWidth = tw;
      textHeight = font.getHeight();
    }
    else {
      font = null;
      final int[] tw = new int[numButtons];
      for (int i=0; i<numButtons; i++)
        tw[i] = 0;
      textWidth = tw;
      textHeight = 0;
    }

    // LOW word-wrapping

    if (alignType == ALIGN_HORIZONTAL)
      totWidth = spacing * (numButtons-1) + imageW * numButtons + totw;
    else if (alignType == ALIGN_VERTICAL || alignType == ALIGN_VERTICAL_SCROLL) {
      int tw = 0;
      for (int i=0; i<numButtons; i++)
        if (imageW + textWidth[i] > tw)
          tw = imageW + textWidth[i];
      totWidth = tw;
    }
    // no else
    else
      totWidth = 0;

    if (alignType == ALIGN_HORIZONTAL)
      totHeight = Math.max(imageH, textHeight);
    else if (alignType == ALIGN_VERTICAL) {
      int th = 0;
      for (int i=0; i<numButtons; i++)
        th += Math.max(images[i].getHeight(), textHeight);
      totHeight = th + spacing * (numButtons-1);
    }
    // TODO support spacing for ALIGN_VERTICAL_SCROLL
    else if (alignType == ALIGN_VERTICAL_SCROLL) {
      int th = 0;
      for (int i=0; i<numButtons; i++)
        th += Math.max(images[i].getHeight(), textHeight);
      totHeight = th;
      scrolled = 0;
      if (totHeight < mapHeight)
        scrolled = (mapHeight-totHeight)>>1;
    }
    // no else
    else
      totHeight = 0;
  }
  
  public void prev() {
    if (selected > 0) {
      selected--;
      if (alignType == ALIGN_VERTICAL_SCROLL)
        doScroll();
    }
  }
  
  public void next() {
    if (selected < numButtons-1) {
      selected++;
      if (alignType == ALIGN_VERTICAL_SCROLL)
        doScroll();
    }
  }
  
  public void setImages(Image[][] images) {
    setImages(images[0], images[1], images[2]);
  }
  
  public void setImages(Image[] images, Image[] images2, Image[] images3) {
    for (int i=0; i<images.length; i++) {
      this.images[i] = images[i];
      this.images2[i] = images2[i];
      this.images3[i] = images3[i];
    }
    updateWidthHeight();
  }
  
  public void setImage(int index, Image image) {
    setImage(index, image, image, image);
  }
  
  public void setImage(int index, Image image, Image image2, Image image3) {
    images[index] = image;
    images2[index] = image2;
    images3[index] = image3;
  }
  
  /**
   * Get index of clicked button (pointer press), wrapper.
   * @param x pointer x
   * @param y pointer y
   * @return index of clicked button, -1 if outside any button
   */
  public int getClickedButtonIndex(int x, int y) {
    if (alignType == ALIGN_HORIZONTAL)
      return getClickedButtonIndexHorizontal(x, y);
    else if (alignType == ALIGN_VERTICAL)
      return getClickedButtonIndexVertical(x, y);
    else if (alignType == ALIGN_VERTICAL_SCROLL)
      return getClickedButtonIndexScroll(x, y);
    // no else
    else
      return 0;
  }
  
  /**
   * Get index of clicked button (pointer press) in horizontal arrangement.
   * @param x pointer x
   * @param y pointer y
   * @return index of clicked button, -1 if outside any button
   */
  private int getClickedButtonIndexHorizontal(int x, int y) {
    if (y < (mapHeight-totHeight)>>1 || y >= ((mapHeight-totHeight)>>1)+totHeight)
      return -1;
    
    int xLeft = (mapWidth-totWidth)>>1;
    // before the first button
    if (x < xLeft)
      return -1;
    
    // check each button
    for (int i=0; i<numButtons; xLeft += (spacing+imageW+textWidth[i++]))
      if (x >= xLeft && x < xLeft+imageW+textWidth[i]) {
        // got it
        selected = i;
        return i;
      }
    
    // not found
    return -1;
  }
    
  /**
   * Get index of clicked button (pointer press) in vertical arrangement.
   * @param x pointer x
   * @param y pointer y
   * @return index of clicked button, -1 if outside any button
   */
  private int getClickedButtonIndexVertical(int x, int y) {
    if (x < (mapWidth-totWidth)>>1 || x >= ((mapWidth-totWidth)>>1)+totWidth)
      return -1;
    
    int yUp = (mapHeight-totHeight)>>1;
    // before the first button
    if (y < yUp)
      return -1;
    
    // check each button
    for (int i=0; i<numButtons; i++) {
      final int totH = Math.max(images[i].getHeight(), textHeight);
      if (y >= yUp && y < yUp+totH) {
        // got it
        selected = i;
        return i;
      }
      yUp += (spacing+totH);
    }
    
    // not found
    return -1;
  }
  
  /**
   * Get index of clicked button (pointer press) in vertical scrolled (full-screen) arrangement.
   * @param x pointer x
   * @param y pointer y
   * @return index of clicked button, -1 if outside any button
   */
  private int getClickedButtonIndexScroll(int x, int y) {
    if (x < (mapWidth-totWidth)>>1 || x >= ((mapWidth-totWidth)>>1)+totWidth)
      return -1;

    int yUp = -scrolled;
    // before the first button
    if (y < yUp)
      return -1;
    
    // check each button
    for (int i=0; i<numButtons; i++) {
      final int totH = Math.max(images[i].getHeight(), textHeight);
      if (y >= yUp && y < yUp+totH) {
        // got it
        selected = i;
        return i;
      }
      yUp += totH;
    }
    
    // not found
    return -1;
  }
  
  /**
   * Getter for pressed.
   *
   * @return Returns the value of pressed.
   */
  public boolean isPressed() {
    return pressed;
  }

  /**
   * Sets the value of pressed.
   *
   * @param pressed The pressed to set.
   */
  public void setPressed(boolean pressed) {
    this.pressed = pressed;
  }

  /**
   * Paint buttons, wrapper.
   * @param g Graphics object
   */
  public void paint(Graphics g) {
    if (alignType == ALIGN_HORIZONTAL)
      paintHorizontal(g);
    else if (alignType == ALIGN_VERTICAL)
      paintVertical(g);
    else if (alignType == ALIGN_VERTICAL_SCROLL)
      paintScroll(g);
  }
  
  private void paintImage(Graphics g, int i, int x, int y) {
    final Image img;
    if (i == selected) {
      if (pressed)
        img = images2[i];
      else if (pointer)
        img = images[i];
      else
        img = images3[i];
    }
    else
      img = images[i]; 
    g.drawImage(img, x, y, Graphics.TOP | Graphics.LEFT);
  }
  
  /**
   * Paint buttons in horizontal arrangement.
   * @param g Graphics object
   */
  private void paintHorizontal(Graphics g) {
    int x = (mapWidth-totWidth)>>1;
    final int y = (mapHeight-totHeight)>>1;

    // background
    if (paintBackground) {
      g.setColor(0xFFFFFF);
      g.fillRect(x, y, totWidth, totHeight);
    }
    
    if (texts != null) {
      g.setFont(font);
      g.setColor(0x000000);
    }
    for (int i=0; i<numButtons; x += (spacing+imageW+textWidth[i++])) {
      final int totW = imageW+textWidth[i];
      g.setClip(x, y, totW, totHeight);
      paintImage(g, i, x, y+((totHeight-imageH)>>1));
      if (texts != null)
        g.drawString(texts[i], x+imageW+SPACING_TEXT, y+((totHeight-textHeight)>>1), Graphics.TOP | Graphics.LEFT);
      if (i == selected && selectionMode == SELECT_RECTANGLE && !pointer) {
        g.setColor(0x000000);
        g.drawRect(x, y, totW-1, totHeight-1);
        g.drawRect(x+1, y+1, totW-3, totHeight-3);
        g.drawRect(x+2, y+2, totW-5, totHeight-5);
      }
    }
  }
  
  /**
   * Paint buttons in vertical arrangement.
   * @param g Graphics object
   */
  private void paintVertical(Graphics g) {
    final int x = (mapWidth-totWidth)>>1;
    int y = (mapHeight-totHeight)>>1;

    // background
    if (paintBackground) {
      g.setColor(0xFFFFFF);
      g.fillRect(x, y, totWidth, totHeight);
    }
    
    if (texts != null) {
      g.setFont(font);
      g.setColor(0x000000);
    }
    for (int i=0; i<numButtons; i++) {
      final int totH = Math.max(images[i].getHeight(), textHeight);
      g.setClip(x, y, totWidth, totH);
      paintImage(g, i, x, y+((totH-images[i].getHeight())>>1));
      if (texts != null)
        g.drawString(texts[i], x+imageW+SPACING_TEXT, y+((totH-textHeight)>>1), Graphics.TOP | Graphics.LEFT);
      if (i == selected && selectionMode == SELECT_RECTANGLE && !pointer) {
        g.setColor(0x000000);
        g.drawRect(x, y, totWidth-1, totH-1);
        g.drawRect(x+1, y+1, totWidth-3, totH-3);
        g.drawRect(x+2, y+2, totWidth-5, totH-5);
      }
      y += (spacing+totH);
    }
  }
  
  /**
   * Paint buttons in vertical scrolled (full-screen) arrangement.
   * @param g Graphics object
   */
  private void paintScroll(Graphics g) {
    final int x = (mapWidth-totWidth)>>1;
    int y = -scrolled;

    // background
    if (paintBackground) {
      g.setColor(0xFFFFFF);
      g.fillRect(x, y, totWidth, totHeight);
    }
    
    if (texts != null) {
      g.setFont(font);
      g.setColor(0x000000);
    }
    for (int i=0; i<numButtons; i++) {
      if (y > mapHeight)
        break;
      final int ih = images[i].getHeight();
      final int totH = Math.max(ih, textHeight);
      if (y + totH >= 0) {
        paintImage(g, i, x, y+((totH-ih)>>1));
        if (texts != null)
          g.drawString(texts[i], x+imageW+SPACING_TEXT, y+((totH-textHeight)>>1), Graphics.TOP | Graphics.LEFT);
        if (i == selected && selectionMode == SELECT_RECTANGLE && !pointer) {
          g.setColor(0x000000);
          g.drawRect(x, y, totWidth-1, totH-1);
          g.drawRect(x+1, y+1, totWidth-3, totH-3);
          g.drawRect(x+2, y+2, totWidth-5, totH-5);
        }
      }
      y += totH;
    }
  }
  
  public void sizeChanged(int w, int h) {
    mapWidth = w;
    mapHeight = h;
  }
  
  public int getType() {
    return type;
  }

  public boolean isHorizontal() {
    return alignType == ALIGN_HORIZONTAL;
  }

  public boolean isVertical() {
    return alignType == ALIGN_VERTICAL || alignType == ALIGN_VERTICAL_SCROLL;
  }
  
  /**
   * Scroll to make sure the selected option is always visible.
   */
  private void doScroll() {
    int y = -scrolled;
    for (int i=0; i<selected; i++)
      y += Math.max(images[i].getHeight(), textHeight);
    
    final int h = Math.max(images[selected].getHeight(), textHeight);
    
    // the option is now between y and y+h
    // first make sure the bottom is within the map height
    if (y+h > mapHeight) {
      final int dif = (y+h-mapHeight);
      scrolled += dif;
      y -= dif;
    }
    // now make sure the top is displayed
    if (y < 0)
      scrolled += y;
  }
  
  /**
   * Add x to scroll, make sure we don't get out of screen.
   * @param x value to add to scroll
   */
  public void doScroll(int x) {
    if (totHeight < mapHeight)
      return;
    
    scrolled += x;
    if (scrolled < 0)
      scrolled = 0;
    if (scrolled >= totHeight-mapHeight)
      scrolled = totHeight-mapHeight-1;
  }
}
